/*
*	File: CoreSubMesh.cpp
*	Date: 12/18/2011
*	Author: Ivan Gao
*	Desc:
*/
#include "CoreSubMesh.h"
SCoreSubMesh::SCoreSubMesh () : m_nCoreMaterialThreadId (0), m_nLodCount (0), m_bHaveNonWhiteVertexColors (false)
{

}

SCoreSubMesh::~SCoreSubMesh ()
{
	m_vecSubMorphTargetGroupIndex.clear();
	m_vecFace.clear();
	m_vecVertex.clear();
	m_vecPhysicalProperty.clear();
	m_vecvecTextureCoordinate.clear();
	m_vecSpring.clear();
	m_vecTangnetsEnabled.clear();
	m_vecvecTangentSpace.clear();
	SCoreSubMorphTargetVector::iterator itr = m_vecCoreSubMorphTarget.begin();
	while (itr != m_vecCoreSubMorphTarget.end())
	{
		if (*itr)
		{
			delete *itr;
		} // if
	} // while
	m_vecCoreSubMorphTarget.clear();
}

void SCoreSubMesh::SetSubMorphTargetGroupIndexArray(int nLen, const int * pIndexArray)
{
	m_vecSubMorphTargetGroupIndex.reserve (nLen);
	m_vecSubMorphTargetGroupIndex.resize(nLen);
	for (int i = 0; i < nLen; i++)
	{
		m_vecSubMorphTargetGroupIndex[i] = pIndexArray[i];
	} // for
} // SetSubMorphTargetGroupIndexArray

int SCoreSubMesh::GetCoreMaterialThreadId (void) const
{
	return m_nCoreMaterialThreadId;
} // GetCoreMaterialThreadId

int SCoreSubMesh::GetFaceCount() const
{
	return m_vecFace.size();
} // GetFaceCount

int SCoreSubMesh::GetLodCount () const
{
	return m_nLodCount;
} // GetLodCount

int SCoreSubMesh::GetSpringCount () const
{
	return m_vecSpring.size ();
} // GetSrpingCount

bool SCoreSubMesh::IsTangentEnabled(int nMapId) const
{
	if (nMapId >= 0 && nMapId < m_vecTangentsEnabled.size ())
	{
		return m_vecTangentsEnabled[nMapId];
	} // if
	return false;
} // IsTangentEnabled

void SCoreSubMesh::UpdateTangentVector(int v0, int v1, int v2, int nMapId)
{
	STextureCoordinateVector & rCV = m_vecvecTextureCoordinate[nMapId];
	// Step 1. Compute the approximate tangent vector.
	double du1 = rCV[v1].u - rCV[v0].u;
	double dv1 = rCV[v1].v - rCV[v0].v;
	double du2 = rCV[v2].u - rCV[v0].u;
	double dv2 = rCV[v2].v - rCV[v0].v;

	double prod = (du1 * dv2 - dv1 * du2);
	if (fabs (prod) < SIMPLEANIMATION_EPSILON)
	{
		return;
	} // if
	double invProd = 1.0f / prod;
	double x = dv2 * invProd;
	double y = dv1 * invProd;
	SVector3 vec1 = m_vecVertex[v1].positon - m_vecVertex[v0].positon;
	SVector3 vec2 = m_vecVertex[v2].positon - m_vecVertex[v0].positon;
	SVector3 tangent = (vec1 * (float)x) + (vec2 * (float)y);
	// Step 2: Orthonormalize the tangent.
	float component = DotProduct(tangent, m_vecVertex[v0].normal);
	tangent -= (m_vecVertex[v0].normal * component);
	tangent.Normalize();

	// Add the estimated tangent to the overall estimate for the vertex.
	m_vecvecTangentSpace[nMapId][v0].tangent += tangent;
} // UpdateTangentVector

bool SCoreSubMesh::EnableTangents(int nMapId, bool bEnable)
{
	if (nMapId < 0 || nMapId >= (int)m_vecTangentsEnabled.size ())
	{
		return false;
	} // if
	m_vecTangentsEnabled[nMapId] = bEnable;
	if (!bEnable)
	{
		m_vecvecTangentSpace[nMapId].clear ();
	}
	else
	{
		int nVertexCount = m_vecVertex.size ();
		STangentSpaceVector & rVecTangentSpace = m_vecvecTangentSpace[nMapId];
		rVecTangentSpace.reserve (nVertexCount);
		rVecTangentSpace.resize (nVertexCount);
		for (int i = 0; i < nVertexCount; ++i)
		{
			rVecTangentSpace[i].crossFactor = 1.0f;
			rVecTangentSpace[i].tangent = SVector3 (0.0f, 0.0f, 0.0f);
		} // for

		for (int nFaceId = 0; nFaceId < (int)m_vecFace.size (); ++nFaceId)
		{
			SVertexIndex * pIndex = m_vecFace[nFaceId].vertexIndex;
			int v0 = pIndex[0], v1 = pIndex[1], v2 = pIndex[2];
			UpdateTangentVector(v0, v1, v2, nMapId);
			UpdateTangentVector(v1, v2, v0, nMapId);
			UpdateTangentVector(v2, v0, v1, nMapId);
		} // for

		// Normalize the tangent
		for (int i = 0; i < nVertexCount; ++i)
		{
			rVecTangentSpace[i].tangent.Normalize();
		}
	} // if
	return true;
} // EnableTangents

SCoreSubMesh::SFaceVector & SCoreSubMesh::GetFaceVector()
{
	return m_vecFace;
} // GetFaceVector

const SCoreSubMesh::SFaceVector & SCoreSubMesh::GetFaceVector() const
{
	return m_vecFace;
} // GetFaceVector

SCoreSubMesh::SPhysicalPropertyVector & SCoreSubMesh::GetPhysicalPropertyVector()
{
	return m_vecPhysicalProperty;
} // GetPhysicalPropertyVector

const SCoreSubMesh::SPhysicalPropertyVector & SCoreSubMesh::GetPhysicalPropertyVector() const
{
	return m_vecPhysicalProperty;
} // GetPhysicalPropertyVector

SCoreSubMesh::SSpringVector & SCoreSubMesh::GetSpringVector()
{
	return m_vecSpring;
} // GetSrpingVector

const SCoreSubMesh::SSpringVector & SCoreSubMesh::GetSpringVector() const
{
	return m_vecSpring;
} // GetSpringVector

SCoreSubMesh::STextureCoordinateVectorVector & SCoreSubMesh::GetTextureCoordinateVectorVector()
{
	return m_vecvecTextureCoordinate;
}  // GetTextureCoordinateVectorVector

const SCoreSubMesh::STextureCoordinateVectorVector & SCoreSubMesh::GetTextureCoordinateVectorVector() const
{
	return m_vecvecTextureCoordinate;
} // GetTextureCoordinateVectorVector

SCoreSubMesh::STangentSpaceVectorVector & SCoreSubMesh::GetTangentSpaceVectorVector()
{
	return m_vecvecTangentSpace;
} // GetTangentSpaceVectorVector

const SCoreSubMesh::STangentSpaceVectorVector & SCoreSubMesh::GetTangentSpaceVectorVector() const
{
	return m_vecvecTangentSpace;
} // GetTangentSpaceVectorVector

SCoreSubMesh::SVertexVector & SCoreSubMesh::GetVertexVector()
{
	return m_vecVertex;
} // GetVertexVector

const SCoreSubMesh::SVertexVector & SCoreSubMesh::GetVertexVector () const
{
	return m_vecVertex;
} // GetVertexVector

int SCoreSubMesh::GetVertexCount () const
{
	return m_vecVertex.size();
} // GetVertexCount

bool SCoreSubMesh::Reserve(int nVertexCount, int nTextureCoordinateCount, int nFaceCount, int nSpringCount)
{
	try
	{
		m_vecVertex.reserve (nVertexCount);
		m_vecVertex.resize (nVertexCount);

		m_vecTangentsEnabled.reserve (nTextureCoordinateCount);
		m_vecTangentsEnabled.resize (nTextureCoordinateCount);

		m_vecvecTangentSpace.reserve (nTextureCoordinateCount);
		m_vecvecTangentSpace.resize (nTextureCoordinateCount);

		m_vecvecTextureCoordinate.reserve(nTextureCoordinateCount);
		m_vecvecTextureCoordinate.resize (nTextureCoordinateCount);

		for (int i = 0; i < nTextureCoordinateCount; ++i)
		{
			m_vecvecTextureCoordinate[i].reserve (nVertexCount);
			m_vecvecTextureCoordinate[i].resize (nVertexCount);

			if (m_vecTangentsEnabled[i])
			{
				m_vecvecTangentSpace[i].reserve (nVertexCount);
				m_vecvecTangentSpace[i].resize (nVertexCount);
			}
			else
			{
				m_vecvecTangentSpace[i].clear ();
			} // if
		} // for

		m_vecFace.reserve (nFaceCount);
		m_vecFace.resize (nFaceCount);

		m_vecSpring.reserve(nSpringCount);
		m_vecSpring.resize (nSpringCount);

		// Reserve the space for the physical property if we have springs in the core submesh instance.
		if (nSpringCount > 0)
		{
			m_vecPhysicalProperty.reserve (nVertexCount);
			m_vecPhysicalProperty.resize (nVertexCount);
		}
	} // try
	catch (...)
	{
		return false;
	} // Catch any exception
	return true;
} // Reserve

void SCoreSubMesh::SetCoreMaterialThreadId (int nCoreMaterialThreadId)
{
	m_nCoreMaterialThreadId = nCoreMaterialThreadId;
} // SetCoreMaterialThreadId

bool SCoreSubMesh::SetFace (int nFaceId, const Face & face)
{
	if (nFaceId >= 0 && nFaceId < (int)m_vecFace.size ())
	{
		m_vecFace[nFaceId] = face;
		return true;
	} // if
	return false;
} // SetFace


void SCoreSubMesh::SetLodCount (int nLodCount)
{
	m_nLodCount = nLodCount;
} // SetLodCount

bool SCoreSubMesh::SetTangentSpace (int nVertexId, int nMapId, const SVector3 & tangent, float crossFactor)
{
	if (nVertexId < 0 || nVertexId >= (int)m_vecVertex.size ())
	{
		return false;
	} // if
	if (nMapId < 0 || nMapId >= (int)m_vecvecTangentSpace.size())
	{
		return false;
	} // if
	if (!m_vecTangentsEnabled[nMapId])
	{
		return false;
	} // if
	m_vecvecTangentSpace[nMapId][nVertexId].tangent = tangent;
	m_vecvecTangentSpace[nMapId][nVertexId].crossFactor = crossFactor;
	return true;
} // SetTangentSpace

bool SCoreSubMesh::SetPhysicalProperty(int nVertexId, const PhysicalProperty & rProperty)
{
	if (nVertexId < 0 || nVertexId > (int)m_vecVertex.size ())
	{
		return false;
	} // if
	m_vecPhysicalProperty[nVertexId] = rProperty;
	return true;
} // SetPhysicalProperty

bool SCoreSubMesh::SetSpring (int nSpringId, const Spring & rSpring)
{
	if (nSpringId < 0 || nSpringId >= (int)m_vecSpring.size ())
	{
		return false;
	} // if
	m_vecSpring[nSpringId] = rSpring;
	return true;
} // SetSpring

bool SCoreSubMesh::SetTextureCoordinate(int nVertexId, int nTextureId, const TextureCoordinate & rTC)
{
	if (nVertexId < 0 || nVertexId >= (int)m_vecVertex.size ())
	{
		return false;
	} // if
	if (nTextureId < 0 || nTextureId >= (int)m_vecvecTextureCoordinate.size ())
	{
		return false;
	} // if
	m_vecvecTextureCoordinate[nTextureId][nVertexId] = rTC;
	return true;
} // SetTextureCoordinate

bool SCoreSubMesh::SetVertex (int nVertexId, const Vertex & rVertex)
{
	if (nVertexId < 0 || nVertexId >= (int)m_vecVertex.size ())
	{
		return false;
	} // if
	m_vecVertex[nVertexId] = rVertex;
} // SetVertex

int SCoreSubMesh::AddCoreSubMorphTarget(SCoreSubMorphTarget * pCoreSubMorphTarget)
{
	int nSubMorphTargetId = m_vecCoreSubMorphTarget.size ();
	m_vecCoreSubMorphTarget.push_back(pCoreSubMorphTarget);
	pCoreSubMorphTarget->SetCoreSubMesh (this);
	return nSubMorphTargetId;
} // AddCoreSubMorphTarget

SCoreSubMorphTarget * SCoreSubMesh::GetCoreSubMorphTarget(int nMorphTargetId)
{
	if (nMorphTargetId < 0 || nMorphTargetId >= (int)m_vecCoreSubMorphTarget.size ())
	{
		return NULL;
	} // if
	return m_vecCoreSubMorphTarget[nMorphTargetId];
} // GetCoreSubMorphTarget

const SCoreSubMorphTarget * SCoreSubMesh::GetCoreSubMorphTarget(int nMorphTargetId) const
{
	if (nMorphTargetId < 0 || nMorphTargetId >= (int)m_vecCoreSubMorphTarget.size ())
	{
		return NULL;
	} // if
	return m_vecCoreSubMorphTarget[nMorphTargetId];
} // GetCoreSubMorphTarget

int SCoreSubMesh::GetCoreSubMorphTargetCount() const
{
	return m_vecCoreSubMorphTarget.size ();
} // GetCoreSubMorphTargetCount

SCoreSubMesh::SCoreSubMorphTargetVector & SCoreSubMesh::GetCoreSubMorphTargetVector()
{
	return m_vecCoreSubMorphTarget;
} // GetCoreSubMorphTargetVector

const SCoreSubMesh::SCoreSubMorphTargetVector & SCoreSubMesh::GetCoreSubMorphTargetVector() const
{
	return m_vecCoreSubMorphTarget;
} // GetCoreSubMorphTargetVector

void SCoreSubMesh::Scale (float scale)
{
	// rescale all vertices.
	for (size_t i = 0; i < m_vecVertex.size (); ++i)
	{
		m_vecVertex[i].positon *= scale;
	} // for

	// Also scale any morph target vertices that may be present
	for (size_t morphId = 0; morphId < m_vecCoreSubMorphTarget.size (); ++morphId)
	{
#pragma message (TODO("core sub morph target scale unimplemented."));
	} // for

	if (!m_vecSpring.empty ())
	{
#pragma message (TODO("spring scale unimplemented."))
	}
} // Scale

